package game.objects.npc;
import game.Game;
import game.ai.fsm.Condition;
import game.ai.fsm.State;
import game.objects.GOPlayer;
import game.objects.GameObject;

import java.util.HashMap;
import java.util.Map.Entry;
import java.util.Vector;

import physics.collision.RayCastInfo;

import tools.Floatmath;
import tools.Help;
import tools.Point;
/**
 * Abstract class that defines basics for a finit-state-machine driven non-player-character
 * 
 * @author jonathan
 * 
 */
public abstract class FSMNPC extends GameObject {
	public static long TICKSPERSEC = 1000 / 1;
	protected long lasttick;
	protected transient HashMap<String, State> states;
	protected transient HashMap<String, Condition> conditions;
	protected transient Vector<Condition> currentConditions;
	protected transient State currentState;
	public FSMNPC(Game g, Point pos) {
		super(g, pos);
		states = new HashMap<String, State>();
		conditions = new HashMap<String, Condition>();
		currentConditions = new Vector<Condition>();
		lasttick = (long) (System.currentTimeMillis() + (long) (Floatmath.random() * 1000));
	}
	public void handle() {
		if (currentState != null) {
			clearConditions();
			currentState.handle();
		}
		else {
			return;
		}
		if (System.currentTimeMillis() - lasttick > TICKSPERSEC) {
			clearConditions();
			currentState.handle();
			this.updateCurrentConditions();
			this.checkConditions();
			currentState = currentState.getNext();
			lasttick = System.currentTimeMillis();
		}
	}
	public Vector<Point> getPlayerPositions(Point pos, float maxdist) {
		Vector<GameObject> pls = game.getAllPlayer();
		Vector<Point> positions = new Vector<Point>();
		float maxdistsq = maxdist * maxdist;
		for (GameObject go : pls) {
			Point p = ((GOPlayer) go).getPosition();
			if (p != null && pos.squaredDistance(p) < maxdistsq)
				positions.add(p);
		}
		return positions;
	}
	public Vector<Point> getVisiblePlayerPositions(Point pos, float offset, float maxdist) {
		Vector<Point> ppos = getPlayerPositions(pos, maxdist);
		Vector<Point> positions = new Vector<Point>();
		for (Point p : ppos) {
			Point dir = p.sub(pos);
			RayCastInfo ri = this.castRay(pos, dir, offset);
			if (ri != null && game.getContainingGO(ri.body) instanceof GOPlayer) {
				positions.add(p);
			}
		}
		return positions;
	}
	public void addState(State state) {
		this.states.put(state.name, state);
	}
	public void addCondition(Condition condition) {
		this.conditions.put(condition.name, condition);
	}
	public State getState(String statename) {
		State s = states.get(statename);
		if (s != null) {
			return s;
		}
		Help.p("Error not such state:" + statename);
		return null;
	}
	protected void clearConditions() {
		for (Entry<String, Condition> mc : conditions.entrySet()) {
			mc.getValue().fulfilled = false;
		}
	}
	protected void updateCurrentConditions() {
		currentConditions.clear();
		for (Condition c : currentState.conditions) {
			currentConditions.add(c);
		}
	}
	protected void checkConditions() {
		for (Condition c : this.currentConditions) {
			c.check();
		}
	}
	public Condition getCondition(String condname) {
		Condition c = conditions.get(condname);
		if (c != null) {
			return c;
		}
		Help.p("Error not such codition:" + condname);
		return null;
	}
}
